package server

import (
	"context"
	"fmt"
	"longmen/server/config/global"
	_const "longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/db_sports"
	"longmen/server/pkg/db/mysql/models/models_main"
	"longmen/server/pkg/db/redis/cache"
	"longmen/server/pkg/protobuf"
	"math"
	"time"

	"github.com/spf13/cast"
	"gorm.io/gorm"
)

/**
* @Author:admin
* @Description:请求有料列表
* @Param :
* @Return :
* @Date: 15:29 2022/6/17
 */
func (s *ExpertServer) GetExpertPlanList(ctx context.Context, expertPlanParam *protobuf.ExpertPlanListReq) (*protobuf.ExpertPlanListResp, error) {
	list, _ := db_sports.GetExpertPlanListByParam(expertPlanParam)
	if len(list) > 0 {
		param, _ := db_sports.GetMatchListByParam(getListAllId(list))
		if param != nil && len(param) > 0 {
			//赛事表中查询出的值 转换成MAP
			keyIntMap := listToMap(param)
			for _, targetScr := range list {
				//直接从MAP中取for 循环次数稳定0(1)
				sources := keyIntMap[int(targetScr.MatchId)]
				if sources != nil {
					mergeRetVal(targetScr, sources)
				}
			}
		}
		//从缓存中取比分
		expertCacheScore(list)
	}
	return &protobuf.ExpertPlanListResp{Item: list}, nil
}

func expertCacheScore(list []*protobuf.ItemExpertPlan) {
	var footBallMatchIds []string
	var basketBallMatchIds []string
	for _, val := range list {
		switch val.SportId {
		case string(_const.FOOT_BALL_SPORT_ID):
			footBallMatchIds = append(footBallMatchIds, fmt.Sprintf(_const.MATCH_FOOTBALL_SCORE_CACHE_PRE+"%s", cast.ToString(val.MatchId)))
		case string(_const.BASKET_BALL_SPORT_ID):
			basketBallMatchIds = append(basketBallMatchIds, fmt.Sprintf(_const.MATCH_BASKETBALL_SCORE_CACHE_PRE+"%s", cast.ToString(val.MatchId)))
		}
	}
	footBallRedisCache := getFootBallBatchRedisCache(footBallMatchIds)
	basketBallRedisCache := getBasketBallBatchRedisCache(basketBallMatchIds)
	//从map 中找数据
	for _, val := range list {
		//找足球的比分
		if value, ok := footBallRedisCache[int64(val.MatchId)]; ok {
			val.AwayScore = int32(value.AwayScore.Score)
			val.HomeScore = int32(value.HomeScore.Score)
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
		//找篮球的比分
		if value, ok := basketBallRedisCache[int64(val.MatchId)]; ok {
			val.AwayScore = int32(value.AwayScore.Score)
			val.HomeScore = int32(value.HomeScore.Score)
			//比赛状态 暂时未有写入
			//val.MatchStatus = uint32(value.MatchStatus)
		}
	}
}

/**
* @Author:admin
* @Description:合并两个查询结果的值
* @Param :
* @Return :
* @Date: 14:21 2022/6/21
 */
func mergeRetVal(targetScr *protobuf.ItemExpertPlan, sources *protobuf.ItemExpertPlan) {
	targetScr.MatchTime = sources.MatchTime
	targetScr.Comp = sources.Comp
	targetScr.Home = sources.Home
	targetScr.Away = sources.Away
	targetScr.HomeScore = sources.HomeScore
	targetScr.AwayScore = sources.AwayScore
	targetScr.MatchStatus = sources.MatchStatus
	targetScr.MatchTitle = sources.MatchTitle
	targetScr.CountDown = sources.CountDown
	targetScr.SportId = sources.SportId
	targetScr.HomeLogo = sources.HomeLogo
	targetScr.AwayLogo = sources.AwayLogo
	targetScr.RealMatchId = sources.MatchId
}

/**
* @Author:admin
* @Description: 把LIST转成MAP
* @Param :
* @Return :
* @Date: 14:23 2022/6/21
 */
func listToMap(list []*protobuf.ItemExpertPlan) map[int]*protobuf.ItemExpertPlan {
	m := make(map[int]*protobuf.ItemExpertPlan)
	for _, val := range list {
		m[int(val.MatchId)] = val
	}
	return m
}

func getListAllId(item []*protobuf.ItemExpertPlan) []int {
	ids := make([]int, 0)
	for _, val := range item {
		ids = append(ids, int(val.MatchId))
	}
	return ids
}

/**
* @Author:admin
* @Description: 有料id详情
* @Param :
* @Return :
* @Date: 15:32 2022/6/17
 */
func (s *ExpertServer) GetExpertPlan(ctx context.Context, expertPlanReq *protobuf.ExpertPlanReq) (*protobuf.ItemExpertPlan, error) {
	planInfo, err := db_sports.GetExpertPlanItemByParam(expertPlanReq.Id, expertPlanReq.UserId)
	if err != nil {
		return nil, fmt.Errorf("%s查询会员购买记录失败%s", util.Convert2(), err)
	}
	param, _ := db_sports.GetMatchListByParam([]int{int(planInfo.MatchId)})
	//合并两个结果集
	if param != nil && len(param) > 0 {
		mergeRetVal(planInfo, param[0])
	}
	//如果没有购买记录，并且有料必需要购买才会显示 关键信息隐藏
	donePay := false
	if expertPlanReq.UserId > 0 {
		donePay = isPay(expertPlanReq.UserId, int64(expertPlanReq.Id))
	}

	//如果是免费也需要直接显示不需要隐藏
	/*if planInfo.FeeType == util.FEE_TYPE_FREE {
		donePay = true
	}
	*/
	//如果是免费，并且已完赛应显示
	/*	if (planInfo.FeeType == util.FEE_TYPE_FREE) && (param[0].MatchStatus==util.SPROT_DONE_MATCH_STATUS){
		donePay=true
	}*/
	err = hidePlanInfo(planInfo, donePay)
	if err != nil {
		return nil, fmt.Errorf("%s查询信息失败%s", util.Convert2(), err)
	}
	//设置玩法
	err = setPlayItem(planInfo, donePay)
	if err != nil {
		return nil, fmt.Errorf("%s查询游戏列表失败%s", util.Convert2(), err)
	}
	if donePay {
		planInfo.IsPay = _const.FEE_IS_PAY
	} else {
		planInfo.IsPay = _const.FEE_NOT_PAY
	}
	//更改比缓存比分
	expertCacheScore([]*protobuf.ItemExpertPlan{planInfo})
	return planInfo, nil
}

/**
* @Author:admin
* @Description: //支付有料
* @Param :
* @Return :
* @Date: 10:15 2022/6/18
 */
func (s *ExpertServer) PayExpertPlanFee(ctx context.Context, expertPlan *protobuf.ExpertPlanFeeReq) (*protobuf.ExpertPlanFeeResp, error) {

	//如果有记录，不用再次支付
	if isPay(expertPlan.UserId, int64(expertPlan.ExpertPlanId)) {
		return expertPlanFeeRet(_const.RPC_REPEAT_PAY, fmt.Errorf("%s已支付%s", util.Convert2()).Error()), nil
	}

	//查询需要支付的费用
	fee, err := db_sports.QueryExpertPlanFee(expertPlan.ExpertPlanId)
	if err != nil {
		return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s查询有料信息失败%s", util.Convert2(), err).Error()), nil
	}
	//免费不能支付
	/*	if fee.FeeType == util.FEE_TYPE_FREE {
		return expertPlanFeeRet(e, util.RPC_FAILED, fmt.Errorf("%s支付失败,该信息免费%s", common.Convert2(), err).Error()), nil
	}*/

	//开赛不能支付
	/*matchTime := repository.QueryMatchesMatchTime(int(fee.MatchId))
	if matchTime < timehelper.Now().Unix() {
		return expertPlanFeeRet(e, util.RPC_FAILED, fmt.Errorf("%s支付失败,该赛事已开赛,不能购买%s", common.Convert2(), err).Error()), nil
	}*/
	//免费也需要记录一条记录
	if fee.FeeType == _const.FEE_TYPE_FREE {
		expertPlan.FeeType = _const.FEE_TYPE_GOLD
		fee.FeePay = 0
		return payExpertFree(expertPlan, fee), nil
	} else {
		return payExpertFee(expertPlan, fee), nil
	}

}

/**
 * @Author admin
 * @Description //免费支付流程 需要查询会是否有关注，如果没有关注需要关注
 * @version: v1.8.0
 * @Date 10:17 2022/7/26
 * @Param
 * @return
 **/
func payExpertFree(expertPlan *protobuf.ExpertPlanFeeReq, fee *models_main.ExpertPlan) *protobuf.ExpertPlanFeeResp {
	session := global.GetX().Begin()
	code := int32(_const.RPC_FAILED)
	err := db_sports.InsertExpertPayRecord(session, generatePayExpertModel(expertPlan, fee))
	if err != nil {
		session.Rollback()
		return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s操作失败%s", util.Convert2(), err).Error())
	}

	count, err1 := db_sports.CountAttentionExpertByUserAndExpert(session, expertPlan.UserId, fee.ExpertId)
	if err1 != nil {
		session.Rollback()
		return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s操作失败%s", util.Convert2(), err).Error())
	}
	//没有被关注
	matchFavorite := generateExpertAttention(int(expertPlan.UserId), int(fee.ExpertId))
	if count <= 0 {
		code = commitSubscribeExpertAttention(session, matchFavorite)
	} else {
		if err := session.Commit().Error; err != nil {
			session.Rollback()
			code = _const.RPC_FAILED
		}
		code = _const.RPC_SUCCEED
	}
	return expertPlanFeeRet(code, fmt.Errorf("%操作成功%s", util.Convert2(), err).Error())
}

/**
 * @Author admin
 * @Description //付费支付流程
 * @version: v1.8.0
 * @Date 10:18 2022/7/26
 * @Param
 * @return
 **/
func payExpertFee(expertPlan *protobuf.ExpertPlanFeeReq, fee *models_main.ExpertPlan) *protobuf.ExpertPlanFeeResp {
	//开始支付 开始事务
	session := global.GetX().Begin()
	//更新记录
	err := db_sports.InsertExpertPayRecord(session, generatePayExpertModel(expertPlan, fee))
	if err != nil {
		session.Rollback()

		return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s更新记录失败%s", util.Convert2(), err).Error())
	}
	//更新会员余额与钱包
	_, _, err = db_main.UpdateAmountByTx(int(expertPlan.UserId), -int(fee.FeePay), getFundsType(fee.FeeType), session, _const.WALLET_BALANCE_HIDE, produceProfit(expertPlan.UserId, int64(fee.UserId), -fee.FeePay, fee.FeeType, _const.FINANCE_CREDIT))

	if err != nil {
		if err.Error() == global.Lang.T("insufficient_balance") || err.Error() == global.Lang.T("insufficient_balance_or_invalid_amount") {
			session.Rollback()
			return expertPlanFeeRet(_const.RPC_BALANCE_NOT_ENOUGH, global.Lang.T("insufficient_balance"))
		} else {
			session.Rollback()
			return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s%s%s", util.Convert2(), global.Lang.T("balance_update_failed"), err).Error())
		}
	}

	//更新专家会员余额与钱包
	//_, _, err = repository.UpdateAmountByTx(fee.UserId, int(fee.FeePay), getFundsType(fee.FeeType), session, util2.WALLET_BALANCE_HIDE, produceProfit(int64(fee.UserId), expertPlan.UserId, fee.FeePay, fee.FeeType, util2.FINANCE_DEBIT))
	//
	//if err != nil {
	//	if err.Error() == util2.BalanceNotEnough.Error() || err.Error() == util2.BalanceNotEnoughFail.Error() {
	//		session.Rollback()
	//		return expertPlanFeeRet(util2.RPC_BALANCE_NOT_ENOUGH, "余额不足")
	//	} else {
	//		session.Rollback()
	//		return expertPlanFeeRet(util2.RPC_FAILED, fmt.Errorf("%s更新余额的失败%s", common.Convert2(), err).Error())
	//	}
	//}

	//提交事务
	if err = session.Commit().Error; err != nil {
		session.Rollback()
		return expertPlanFeeRet(_const.RPC_FAILED, fmt.Errorf("%s购买失败%s", util.Convert2(), err).Error())
	}
	cache.DelUserFullCache(int(expertPlan.UserId))
	return expertPlanFeeRet(_const.RPC_SUCCEED, "操作成功")
}

/**
* @Author:admin
* @Description:返回有料中赛事列表菜单
* @Param :
* @Return :
* @Date: 9:29 2022/6/30
 */

func (s *ExpertServer) GetExpertCompMenu(ctx context.Context, empty *protobuf.Empty) (*protobuf.ExpertCompResp, error) {
	res := &protobuf.ExpertCompResp{}
	//先从redis中取，如果不能取到从资料库中取
	err := getRedisCache(&res, _const.EXPERT_INDEX_MENU_CACHE_KEY)
	if err != nil {
		if expertComps, err := db_sports.GetCompListByExpert(); err == nil {
			res.List = expertComps
			setRedisCache(res, _const.EXPERT_INDEX_MENU_CACHE_KEY, _const.EXPERT_INDEX_MENU_CACHE_EXPIRY_KEY)
		}
	}
	return res, nil
}

/**
* @Author:admin
* @Description://赛事详情有料列表
* @Param :
* @Return :
* @Date: 9:34 2022/6/30
 */

func (s *ExpertServer) GetExpertFromMatch(ctx context.Context, matchResp *protobuf.ExpertFromMatchReq) (*protobuf.ExpertPlanListResp, error) {
	list, _ := db_sports.GetExpertFromMatch(matchResp)
	if len(list) > 0 {
		param, _ := db_sports.GetMatchListByParam(getListAllId(list))
		if param != nil && len(param) > 0 {
			//赛事表中查询出的值 转换成MAP
			keyIntMap := listToMap(param)
			for _, targetScr := range list {
				//直接从MAP中取for 循环次数稳定0(1)
				sources := keyIntMap[int(targetScr.MatchId)]
				if sources != nil {
					mergeRetVal(targetScr, sources)
				}
			}
		}
		//从缓存中取比分
		expertCacheScore(list)
	}
	return &protobuf.ExpertPlanListResp{Item: list}, nil
}

func produceProfit(userId, expertUserId int64, FeePay int32, feeType int32, financeType int) []*models_main.UserProfit {
	//写入用户消费记录
	giftPriceName := _const.EMPTY_STR
	if feeType == int32(_const.CoinDiamond) {
		giftPriceName = "龙钻"
	} else if feeType == int32(_const.CoinGold) {
		giftPriceName = "龙币"
	}
	tempContent := fmt.Sprintf("会员id(%d)鹰眼消费：%d %s", userId, int(math.Abs(float64(FeePay)/100)), giftPriceName)
	if FeePay > 0 {
		tempContent = fmt.Sprintf("会员专家id(%d)鹰眼收入：%d %s", userId, int(math.Abs(float64(FeePay)/100)), giftPriceName)
	}
	userProfit := &models_main.UserProfit{
		Uid:         int(userId),
		CoinCount:   int(FeePay),
		Content:     tempContent,
		Type:        financeType,
		ConsumeType: 2,
		Resid:       0,
		Count:       0,
		CreateTime:  time.Now(),
		CoinType:    int(feeType),
		FUid:        int(expertUserId),
		OrderNo:     util.GenOrderId("GS", 1),
		Liveid:      0,
	}
	var profitLog []*models_main.UserProfit
	profitLog = append(profitLog, userProfit)
	return profitLog
}

func expertPlanFeeRet(code int32, message string) *protobuf.ExpertPlanFeeResp {
	return &protobuf.ExpertPlanFeeResp{Code: code, Message: message}
}

func generatePayExpertModel(expertPlan *protobuf.ExpertPlanFeeReq, planModel *models_main.ExpertPlan) *models_main.ExpertPayRecord {
	return &models_main.ExpertPayRecord{
		PlanId: int64(expertPlan.GetExpertPlanId()),
		UserId: expertPlan.GetUserId(),
		Amount: -planModel.FeePay,
		Type:   expertPlan.FeeType,
	}
}

func getFundsType(typeVal int32) _const.FundsType {
	var fundsType _const.FundsType
	switch typeVal {
	case 1:
		fundsType = _const.CoinGold
	case 2:
		fundsType = _const.CoinDiamond
	default:
	}
	return fundsType
}

/**
* @Author:admin
* @Description: 内部方法 隐藏消息
* @Param :
* @Return :
* @Date: 16:26 2022/6/17
 */

func isPay(userId, expertId int64) bool {
	nums, _ := db_sports.CountUserPay(userId, expertId)
	if nums > 0 {
		return true
	} else {
		return false
	}
}

func hidePlanInfo(planInfo *protobuf.ItemExpertPlan, donePay bool) error {
	if planInfo.GetFeeType() != _const.FEE_TYPE_FREE {
		//没有购买记录需要隐藏信息
		if !donePay {
			planInfo.Content = _const.EMPTY_STR
		}
	}
	return nil
}

func setPlayItem(planInfo *protobuf.ItemExpertPlan, donePay bool) error {
	playList, err := db_sports.GetExpertPlayListByPlanId(planInfo.GetId())
	if err != nil {
		return fmt.Errorf("%s查询玩法列表失败%s", util.Convert2(), err)
	}
	//未有购买全都改为未开奖
	if !donePay {
		for ind, _ := range playList {
			playList[ind].Recommend = 0
		}
	}
	planInfo.PlayItem = playList
	return nil
}

///v2 版函数从以下开始

/**
 * @Author admin
 * @Description // 热门联赛标签
 * @version: v1.8.0
 * @Date 15:27 2022/7/22
 * @Param
 * @return
 **/
func (s *ExpertServer) GetHotComp(ctx context.Context, empty *protobuf.Empty) (*protobuf.HotCompLabelResp, error) {
	compList, err := db_sports.GetHotComp()
	if err != nil {
		return nil, fmt.Errorf("%s %s", util.Convert2(), err)
	}
	return &protobuf.HotCompLabelResp{List: compList}, nil
}

/**
 * @Author admin
 * @Description //模糊查询
 * @version: v1.8.0
 * @Date 16:22 2022/7/22
 * @Param
 * @return
 **/
func (s *ExpertServer) GetMatchLikeSearch(ctx context.Context, req *protobuf.MatchLikeSearchReq) (*protobuf.MatchLikeSearchResp, error) {
	compList, err := db_sports.GetMatchLikeSearch(req)
	if err != nil {
		return nil, fmt.Errorf("%s %s", util.Convert2(), err)
	}
	return &protobuf.MatchLikeSearchResp{List: compList}, nil
}

/**
 * @Author admin
 * @Description //专家列表
 * @version: v1.8.0
 * @Date 18:34 2022/7/22
 * @Param
 * @return
 **/
func (s *ExpertServer) GetExpertList(ctx context.Context, req *protobuf.ExpertSearchCond) (*protobuf.ExpertInfoResp, error) {
	list, err := db_sports.GetExpertList(req)
	if err != nil {
		return nil, fmt.Errorf("%s %s", util.Convert2(), err)
	}
	return &protobuf.ExpertInfoResp{List: list}, nil
}

/**
 * @Author admin
 * @Description //查询专家信息
 * @version: v1.8.0
 * @Date 18:53 2022/7/22
 * @Param
 * @return
 **/
func (s *ExpertServer) GetExpertById(ctx context.Context, req *protobuf.ExpertByIdReq) (*protobuf.ExpertInfo, error) {
	info, err := db_sports.GetExpertByIdAndAttention(req)
	if err != nil {
		return nil, fmt.Errorf("%s %s", util.Convert2(), err)
	}
	return info, nil
}

/**
 * @Author admin
 * @Description //专家关注与取消关注
 * @version: v1.8.0
 * @Date 10:11 2022/7/25
 * @Param
 * @return
 **/
func (s *ExpertServer) AttentionExpert(ctx context.Context, req *protobuf.AttentionExpertReq) (*protobuf.AttentionExpertResp, error) {
	ret := &protobuf.AttentionExpertResp{}
	switch req.Attention {
	case _const.EXPERT_ATTENTION_SUBSCRIBE:
		ret = subscribeExpertAttention(req)
	case _const.EXPERT_ATTENTION_CANCEL:
		ret = cancelExpertAttention(req)
	}
	ret.IsAttention = int32(db_sports.CountAttention(int(req.UserId), int(req.ExpertId)))
	return ret, nil
}

/**
 * @Author admin
 * @Description //赛事收藏
 * @version: v1.8.0
 * @Date 16:10 2022/7/18
 * @Param
 * @return
 **/
func subscribeExpertAttention(req *protobuf.AttentionExpertReq) *protobuf.AttentionExpertResp {
	ret := &protobuf.AttentionExpertResp{Code: _const.RPC_FAILED}
	matchFavorite := generateExpertAttention(int(req.UserId), int(req.ExpertId))
	par := &protobuf.ExpertByIdReq{ExpertId: int32(req.ExpertId)}
	if expert, err := db_sports.GetExpertById(par); err == nil {
		//专家里的会员ID,不能等于会员ID,不能自已关注自已
		if expert.UserId != req.UserId {
			session := global.GetX().Begin()
			ret.Code = commitSubscribeExpertAttention(session, matchFavorite)
		}
	}
	return ret
}

/**
 * @Author admin
 * @Description //事务提交代码
 * @version: v1.8.0
 * @Date 16:13 2022/7/20
 * @Param
 * @return
 **/
func commitSubscribeExpertAttention(session *gorm.DB, attention *models_main.ExpertAttention) int32 {
	if row := db_sports.InsertAttentionExpert(session, attention); row == 0 {
		session.Rollback()
		return _const.RPC_FAILED
	}
	if row := db_sports.UpdateAttentionFansExpert(session, attention.ExpertId, 1); row == 0 {
		session.Rollback()
		return _const.RPC_FAILED
	}
	row, err := db_sports.CountAttentionExpert(session, attention.UserId)
	if err != nil {
		session.Rollback()
		return _const.RPC_FAILED
	}
	if row > 0 {
		if row1 := db_sports.UpdateAttentionExpert(session, attention.UserId, 1); row1 == 0 {
			session.Rollback()
			return _const.RPC_FAILED
		}
	}
	if err := session.Commit().Error; err != nil {
		session.Rollback()
		return _const.RPC_FAILED
	}
	return _const.RPC_SUCCEED
}

/**
 * @Author admin
 * @Description //赛事收藏取消
 * @version: v1.8.0
 * @Date 16:11 2022/7/18
 * @Param
 * @return
 **/
func cancelExpertAttention(req *protobuf.AttentionExpertReq) *protobuf.AttentionExpertResp {
	ret := &protobuf.AttentionExpertResp{}
	session := global.GetX().Begin()
	if row := db_sports.DelAttentionExpert(session, req); row == 0 {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	if row := db_sports.UpdateAttentionFansExpert(session, int(req.ExpertId), -1); row == 0 {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	row, err := db_sports.CountAttentionExpert(session, int(req.UserId))
	if err != nil {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	if row > 0 {
		if row = db_sports.UpdateAttentionExpert(session, int(req.UserId), -1); row == 0 {
			session.Rollback()
			ret.Code = _const.RPC_FAILED
			return ret
		}
	}
	if err := session.Commit().Error; err != nil {
		session.Rollback()
		ret.Code = _const.RPC_FAILED
		return ret
	}
	ret.Code = _const.RPC_SUCCEED
	return ret
}

/**
 * @Author admin
 * @Description //生成一个赛事收藏Model
 * @version: v1.8.0
 * @Date 16:16 2022/7/20
 * @Param
 * @return
 **/
func generateExpertAttention(userId, ExpertId int) *models_main.ExpertAttention {
	return &models_main.ExpertAttention{
		UserId:     userId,
		ExpertId:   ExpertId,
		CreateTime: time.Now().Unix(),
	}
}
